﻿/**
 * @file SolverControlerBase.cpp
 * @brief 求解器控制基类源文件
 * @author FastCAE研发小组(fastcae@diso.cn)
 * @version 2.5.0
 * @date 2022-03-12 15:34
 * @copyright Copyright (c) Since 2020 青岛数智船海科技有限公司  All rights reserved.
 *
 * ============================================================================
 * Program:   FastCAE
 *
 * Copyright (c) Since 2020 青岛数智船海科技有限公司  All rights reserved.
 * See License or http://www.fastcae.com/ for details.
 *
 * BSD 3-Clause License
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.
 * ==================================================================================
 */
#include "SolverControlerBase.h"
#include "MainWindow/MainWindow.h"
#include "ModuleBase/processBar.h"
#include "PostWidgets/RealTimeWindowBase.h"
#include "ModelData/modelDataSingleton.h"
#include "ModelData/modelDataBaseExtend.h"
#include "ConfigOptions/ConfigOptions.h"
#include "ConfigOptions/SolverConfig.h"
#include "ConfigOptions/SolverInfo.h"
#include "IO/SolverIO.h"
#include <QMessageBox>
#include <QCoreApplication>
#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include "PythonModule/PyAgent.h"

namespace SolverControl {

	SolverControlBase::SolverControlBase(GUI::MainWindow*		   mainwindow,
										 ConfigOption::SolverInfo* solver,
										 ModelData::ModelDataBase* m, bool sonly)
		: _mainWindow(mainwindow)
		, _solver(solver)
		, _model(m)
		, _solveOnly(sonly)
	{
		connect(&_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readSolverOutput()));
		connect(this, SIGNAL(sendMessage(QString)), _mainWindow,
				SIGNAL(printMessageToMessageWindow(QString)));
		connect(this, SIGNAL(solverStarted(QWidget*)), _mainWindow,
				SIGNAL(addProcessBarSig(QWidget*)));
		connect(_mainWindow, SIGNAL(stopSolve(QWidget*)), this, SLOT(stopSolver(QWidget*)));
		connect(&_process, SIGNAL(finished(int, QProcess::ExitStatus)), this,
				SLOT(processFinished(int, QProcess::ExitStatus)));
		connect(this, SIGNAL(processFinish(int)), _mainWindow, SIGNAL(processFinished(int)));
		connect(this, SIGNAL(openRealTime(Post::RealTimeWindowBase*, int)), _mainWindow,
				SIGNAL(openRealTimeWindowSig(Post::RealTimeWindowBase*, int)));
	}
	SolverControlBase::~SolverControlBase()
	{
		if(_processBar != nullptr)
			delete _processBar;
	}

	void SolverControlBase::setSolverDescription(QString des)
	{
		_description = des;
	}

	void SolverControlBase::startSolver()
	{
		QString solverPath = _solver->getExePath();
		qDebug() << solverPath;
		QFileInfo info(solverPath);
		int		  id = _model->getID();

		if((!info.exists()) || (!info.isFile())) {
			QMessageBox::warning(nullptr, tr("Warning"),
								 tr("Solver Path Error! Solve Path : %1").arg(solverPath));
			emit processFinish(id);
			return;
		}
		bool ok = preProcess();
		if(!ok) {
			QMessageBox::warning(nullptr, tr("Warning"), tr("Input file write failed !"));
			emit processFinish(id);
			return;
		}

		QString oldDir	  = "";

		QString startPath = _model->getPath();

		if(_solver->getType() == ConfigOption::SelfDevelop) {
			QDir dir(startPath);
			if(dir.exists() && (!startPath.isEmpty())) {
				oldDir = QDir::currentPath();
				QDir::setCurrent(startPath);
			}
		}

		_processBar	 = new ModuleBase::ProcessBar(_mainWindow, _description, false);

		QString args = _solver->getParameter();
		args.replace("%modelpath%", startPath, Qt::CaseInsensitive);
		/*QRegExp regExp("%.*%");
		regExp.setMinimal(true);
		// int pos = regExp.indexIn(args);
		QStringList variables = regExp.capturedTexts();
		for(int i = 0; i < variables.size(); ++i) {
			QString variable = variables.at(i);
			QString va		 = variable.remove("%");
			if(va.toLower() == "modelpath")
				args.replace(variables.at(i), startPath);
		}*/

		// 		QString startProcess = solverPath + " " + args;
		// 		if (solverPath.contains(" "))
		// 			startProcess = QString("\"%1\"").arg(startProcess);

		//		qDebug() << startProcess;
		// 		if (!_args.isEmpty())
		// 			startProcess = startProcess + " " + _args;
		//		qDebug() << startProcess;

		_process.start(solverPath, args.split(" ", QString::SkipEmptyParts));

		emit							solverStarted(_processBar);

		ModelData::ModelDataBaseExtend* extend =
			dynamic_cast<ModelData::ModelDataBaseExtend*>(_model);
		if(extend != nullptr && !_solveOnly) {
			QList<ConfigOption::PostCurve*> curves = extend->getMonitorCurves();
			if(curves.size() > 0) {
				int						  id = _model->getID();
				Post::RealTimeWindowBase* w	 = new Post::RealTimeWindowBase(_mainWindow, id);
				emit					  openRealTime(w, id);
			}
		}
		if(_solver->getType() == ConfigOption::SelfDevelop) {
			if(!oldDir.isEmpty())
				QDir::setCurrent(oldDir);
		}
	}
	void SolverControlBase::readSolverOutput()
	{
		_outputBuffer	= QString::fromLocal8Bit(_process.readAll());
		QString process = _solver->getProcessKeyWord() + "(\\d+)(?:\\s*)";
		//		qDebug() << process;
		QRegExp rx(process);
		int		pos = _outputBuffer.indexOf(rx);
		if(pos > -1 && _processBar != nullptr) {
			QString s = rx.cap(1);
			_processBar->setProcess(s.toInt());
		}

		emit sendMessage(_outputBuffer);
	}
	void SolverControlBase::stopSolver(QWidget* w)
	{
		if(w != _processBar)
			return;

		int id = -1;
		if(_model != nullptr)
			id = _model->getID();
		emit removeSolver(id);
		if(!_processFinished) {
			_process.kill();
		}
		_processBar = nullptr;
		//		this->deleteLater();
	}

	void SolverControlBase::stopSolver()
	{
		if(!_processFinished) {
			_process.kill();
		}
	}

	void SolverControlBase::startMPI(int nprocess)
	{
		QString	  solverPath = _solver->getExePath();
		QFileInfo info(solverPath);
		if((!info.exists()) || (!info.isFile())) {
			QMessageBox::warning(nullptr, tr("Warning"),
								 tr("Solver Path Error! Solve Path : %1").arg(solverPath));
			return;
		}
		bool ok = preProcess();
		if(!ok) {
			QMessageBox::warning(nullptr, tr("Warning"), tr("Input file write failed !"));
			return;
		}

		QString oldDir	  = "";

		QString startPath = _model->getPath();

		if(_solver->getType() == ConfigOption::SelfDevelop) {
			QDir dir(startPath);
			if(dir.exists() && (!startPath.isEmpty())) {
				oldDir = QDir::currentPath();
				QDir::setCurrent(startPath);
			}
		}

		_processBar	 = new ModuleBase::ProcessBar(_mainWindow, _description, false);

		QString args = _solver->getParameter();
		QRegExp regExp("%.*%");
		regExp.setMinimal(true);
		// int pos = regExp.indexIn(args);
		QStringList variables = regExp.capturedTexts();
		for(int i = 0; i < variables.size(); ++i) {
			QString variable = variables.at(i);
			QString va		 = variable.remove("%");
			if(va.toLower() == "modelpath")
				args.replace(variables.at(i), startPath);
			//			qDebug() << args;
		}

		// 		QString startProcess = solverPath + " " + args;
		// 		if (solverPath.contains(" "))
		// 			startProcess = QString("\"%1\"").arg(startProcess);

		//		qDebug() << startProcess;
		// 		if (!_args.isEmpty())
		// 			startProcess = startProcess + " " + _args;
		//		qDebug() << startProcess;

		QString c = QString("mpiexec -n %1 %2").arg(nprocess).arg(solverPath);

		_process.start(c);

		emit							solverStarted(_processBar);

		ModelData::ModelDataBaseExtend* extend =
			dynamic_cast<ModelData::ModelDataBaseExtend*>(_model);
		if(extend != nullptr && !_solveOnly) {
			QList<ConfigOption::PostCurve*> curves = extend->getMonitorCurves();
			if(curves.size() > 0) {
				int						  id = _model->getID();
				Post::RealTimeWindowBase* w	 = new Post::RealTimeWindowBase(_mainWindow, id);
				emit					  openRealTime(w, id);
			}
		}
		if(_solver->getType() == ConfigOption::SelfDevelop) {
			if(!oldDir.isEmpty())
				QDir::setCurrent(oldDir);
		}
	}

	void SolverControlBase::setSolveArgs(QString arg)
	{
		_args = arg;
	}

	void SolverControlBase::startSolverClear()
	{
		_solveOnly		   = true;
		_processBar		   = new ModuleBase::ProcessBar(_mainWindow, _description, false);
		QString solverPath = _solver->getExePath();

		_process.start(solverPath, QStringList(_args));

		emit solverStarted(_processBar);
	}

	void SolverControlBase::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
	{
		Q_UNUSED(exitCode);

		_processFinished = true;

		int id			 = -1;
		if(_model != nullptr)
			id = _model->getID();
		emit processFinish(id);

		switch(exitStatus) {
			case QProcess::NormalExit:
				emit sendMessage("************************************");
				emit sendMessage("***** Solving process finished *****");
				emit sendMessage("************************************");

				break;
			case QProcess::CrashExit:
				emit sendMessage("************************************");
				emit sendMessage("***** Solving process Crashed ******");
				emit sendMessage("************************************");
				break;
			default:
				emit sendMessage("***********************************");
				emit sendMessage("***** Solving process Stopped *****");
				emit sendMessage("***********************************");
				break;
		}
		if(_processBar != nullptr) {
			if(_processBar->isBusy())
				_processBar->setProcessRange(0, 100);
			_processBar->setProcess(100);
		}
		postPorocess();
		Py::PythonAgent::getInstance()->unLock();
	}

	bool SolverControlBase::preProcess()
	{
		if(_solveOnly)
			return true;
		bool istemp = _solver->isWriteTemplate();
		if(istemp) {
			QString te	 = _solver->getTemplate();
			QString path = _model->getPath();
			return IO::SolverIO::replaceTemplate(te, path, _model);
		} else {
			QString format = _solver->getInputFormat();
			return IO::SolverIO::writeInpFile(format, _model);
		}
		return false;
	}

	bool SolverControlBase::postPorocess()
	{
		if(_solveOnly)
			return true;
		QString trans = _solver->getTransfer();
		QString path  = _model->getPath();
		return IO::SolverIO::transformFile(trans, path);
	}

} // namespace SolverControl
